﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;

namespace UILibrary
{
    class FactoryLocator
    {
        public static ControlFactory getFactory(string type)
        {
            switch (type)
            {
                case "CheckedListBox":
                    return new CheckBoxControlFactory();
                case "TreeView":
                    return new TreeViewControlFactory();
                default:
                    return new CommonControlFactory();
            }
        }
    }
    public class ControlFactory
    {
        protected delegate void PropValueDelegate(Control ctrl, Hashtable propertyList, PropertyDescriptor myProperty);
        //protected ArrayList m_propertyList = new ArrayList();
        protected ArrayList m_excludePropertyList = new ArrayList();

         public ControlFactory()
        {
            //fill the map of required properties
            //m_propertyList.Add("Enabled");
            //m_propertyList.Add("Text");
            //m_propertyList.Add("Checked");
            
             //we have to ignore some properties
            m_excludePropertyList.Add("Visible");
        }

        public void setPropValue(Control ctrl, Hashtable propertyList, PropertyDescriptor myProperty)
        {
            try
            {
                propertyList.Add(myProperty.Name, myProperty.GetValue(ctrl));
            }
            catch (System.Exception ex)
            {
                Utilities.TraceWrapper.LogException("setPropValue error: " + ex.Message, ex);
            }
        }

        public virtual Hashtable getControlsProperties(Control ctrl)
        {
            return new Hashtable();
        }

        public virtual void fillProperties(Control ctrl, Hashtable propertyList){}

        protected Control createControl(string type, string partialName)
        {
            try
            {
                Control ctrl = null;
                switch (type)
                {
                    case "Label":
                        ctrl = new Label();
                        break;
                    case "LinkLabel":
                        ctrl = new LinkLabel();
                        break;
                    case "Panel":
                        ctrl = new Panel();
                        break;
                    case "PictureBox":
                        ctrl = new PictureBox();
                        break;
                    case "ListView":
                        ctrl = new ListView();
                        break;
                    case "TextBox":
                        ctrl = new TextBox();
                        break;
                    case "CheckedListBox":
                        ctrl = new CheckedListBox();
                        break;
                    case "Button":
                        ctrl = new Button();
                        break;
                    case "RadioButton":
                        ctrl = new RadioButton();
                        break;
                    case "TreeView":
                        ctrl = new TreeView();
                        break;
                    case "CheckBox":
                        ctrl = new CheckBox();
                        break;
                    //case "TabControl":
                    //    ctrl = new TabControl();
                    //    break;
                    //case "TabPage":
                    //    ctrl = new TabPage();
                    //    break;

                    //default:
                    //    Assembly controlAsm = Assembly.LoadWithPartialName(partialName);
                    //    Type controlType = controlAsm.GetType(partialName + "." + type);
                    //    ctrl = (Control)Activator.CreateInstance(controlType);
                    //    break;

                }
                return ctrl;

            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine("create control failed:" + ex.Message);
                return new Control();
            }
        }


        public Control clone(Control original)
        {
            ControlFactory cf = FactoryLocator.getFactory(original.GetType().Name);
            
            Control ctrl = cf.createControl(original.GetType().Name, original.GetType().Namespace);
            if (ctrl != null)
            {
                Hashtable propertyList = cf.getControlsProperties(original);
                cf.fillProperties(ctrl, propertyList);
            }

            return ctrl;
        }
    }

    class CommonControlFactory : ControlFactory
    {
        public override void fillProperties(Control ctrl, Hashtable propertyList)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(ctrl);

            foreach (PropertyDescriptor myProperty in properties)
            {
                if (propertyList.Contains(myProperty.Name))
                {
                    Object obj = propertyList[myProperty.Name];
                    try
                    {
                        myProperty.SetValue(ctrl, obj);
                    }
                    catch (Exception ex)
                    {
                        //do nothing, just continue
                        System.Diagnostics.Trace.WriteLine(ex.Message);
                    }
                }
            }	
        }

        public override Hashtable getControlsProperties(Control ctrl)
        {
            Hashtable propertyList = new Hashtable();
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(ctrl);

            foreach (PropertyDescriptor myProperty in properties)
            {
                try
                {
                    //we get only required properties
                    if ((myProperty.PropertyType.IsSerializable) && (m_excludePropertyList.IndexOf(myProperty.Name) == -1))
                    {
                        if (ctrl.InvokeRequired)
                        {
                            PropValueDelegate Obj = new PropValueDelegate(this.setPropValue);
                            ctrl.BeginInvoke(Obj, new object[] { ctrl, propertyList, myProperty });
                        }
                        else
                            propertyList.Add(myProperty.Name, myProperty.GetValue(ctrl));
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(ex.Message);
                    //do nothing, just continue
                }
            }

            return propertyList;
        }
    }

    class CheckBoxControlFactory : CommonControlFactory
    {
        object[] m_items;

        public override Hashtable getControlsProperties(Control ctrl_orig)
        {
            CheckedListBox ctrl = ctrl_orig as CheckedListBox;
            m_items = new object[ctrl.Items.Count];
            ctrl.Items.CopyTo(m_items, 0);

            return base.getControlsProperties(ctrl_orig);
        }

        public override void fillProperties(Control ctrl_orig, Hashtable propertyList)
        {
            //copy items separately because it's not serializable
            CheckedListBox ctrl = ctrl_orig as CheckedListBox;
            ctrl.Items.Clear();
            ctrl.Items.AddRange(m_items);

            base.fillProperties(ctrl_orig, propertyList);
        }
    }

    class TreeViewControlFactory : ControlFactory
    {
        TreeNode[] m_items;

        public override Hashtable getControlsProperties(Control ctrl_orig)
        {
            TreeView ctrl = ctrl_orig as TreeView;
            m_items = new TreeNode[ctrl.Nodes.Count];
            ctrl.Nodes.CopyTo(m_items, 0);

            return base.getControlsProperties(ctrl_orig);
        }

        public override void fillProperties(Control ctrl_orig, Hashtable propertyList)
        {
            //copy items separately because it's not serializable
            TreeView ctrl = ctrl_orig as TreeView;
            ctrl.Nodes.Clear();

            foreach (TreeNode tr in m_items)
                ctrl.Nodes.Add((TreeNode)tr.Clone());

            base.fillProperties(ctrl_orig, propertyList);
        }
    }

    struct SnapShot
    {
	    public Dictionary<string, Control> m_collectionControls;
    }

    public class SnapshotManager
    {
        Dictionary<string, SnapShot> m_states = new Dictionary<string,SnapShot>();

        private ArrayList GetControls(ContainerControl cc, Type ctype)
        {
            ArrayList al = new ArrayList();
            AddControls(ref al, cc.Controls, ctype);
            return al;
        }

        private void AddControls(ref ArrayList al, Control.ControlCollection cc, Type ctype)
        {
            foreach(Control c in cc)
            {
                if(ctype==null)
                    al.Add(c);
                if(c.HasChildren)
                    AddControls(ref al, c.Controls,ctype);
            }
        }

        private void saveControlCollection(string stateName, ArrayList ctrls)
        {
            SnapShot m_snapshot = new SnapShot();
            m_snapshot.m_collectionControls = new Dictionary<string, Control>();

            foreach (Control ctrl in ctrls)
            {
                ControlFactory cf = new ControlFactory();
                m_snapshot.m_collectionControls.Add(ctrl.Name, cf.clone(ctrl));
            }
            if (m_states.ContainsKey(stateName))
                m_states[stateName] = m_snapshot;
            else
                m_states.Add(stateName, m_snapshot);
        }

        public void takeSnapshot(string stateName, UserControl frm)
        {
            ArrayList al = new ArrayList();
            al = GetControls(frm.GetContainerControl() as ContainerControl, null);
            saveControlCollection(stateName, al);
        }

        public void takeSnapshot(string stateName, Form frm)
        {
            ArrayList al = new ArrayList();
            al = GetControls(frm.GetContainerControl() as ContainerControl, null);
            saveControlCollection(stateName, al);
        }

        public Form getSnapshot(string stateName)
        {
            Form frm = null;

            if (m_states.ContainsKey(stateName))
            {
                frm = new Form();
                foreach (var ctrl in m_states[stateName].m_collectionControls)
                {
                    ControlFactory cf = new ControlFactory();
                    frm.Controls.Add(cf.clone(ctrl.Value));
                }
            }

            return frm;
        }

        private void applySnapshotImpl(string stateName, ArrayList al)
        {
            if (m_states.ContainsKey(stateName))
            {
                foreach (object obj in al)
                {
                    Control ctrl = obj as Control;
                    if (m_states[stateName].m_collectionControls.ContainsKey(ctrl.Name))
                    {
                        Control ctrl_orig = m_states[stateName].m_collectionControls[ctrl.Name];
                        if (ctrl_orig != null)
                        {
                            ControlFactory req_f = FactoryLocator.getFactory(ctrl_orig.GetType().Name);
                            Hashtable hs = req_f.getControlsProperties(ctrl_orig);
                            req_f.fillProperties(ctrl, hs);
                        }
                    }
                }
            }
        }

        public void applySnapshot(string stateName, UserControl frm)
        {
            ArrayList al = new ArrayList();
            al = GetControls(frm.GetContainerControl() as ContainerControl, null);
            applySnapshotImpl(stateName, al);
        }

        public void applySnapshot(string stateName, Form frm)
        {
            ArrayList al = new ArrayList();
            al = GetControls(frm.GetContainerControl() as ContainerControl, null);
            applySnapshotImpl(stateName, al);
        }
    }
}
